"""
Routes and views for the bottle application.
"""

import re
import json
from bottle import route, view, template, request, response, redirect, abort, static_file
from datetime import datetime
import plan
import cfg

from bottle_flash2 import FlashPlugin
from bottle import Bottle

# Flash Setup
app = Bottle()
COOKIE_SECRET = 'super_secret_string'
app.install(FlashPlugin(secret=COOKIE_SECRET))

re_mac = re.compile(r'([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}')


@route('/help')
@view('help')
def about():
    """Renders the help page."""
    return dict(title='Help', year=datetime.now().year)


@route('/')
@route('/home')
@route('/plan')
def plan_list():
    """Renders the plan page."""
    out = request.query.get('out', None)
    if out == 'raw':
        response.headers['content_type'] = 'text/plain'
        sep = request.query.get('sep', ',')
        head = [
            'rack', 'location', 'mac', 'progress_text', 'progress_value',
            'finished'
        ]
        pl = [[
            p.client.rack, p.client.location, p.client.mac, p.progress.text,
            p.progress.value, p.finished
        ] for p in plan.list()]
        pl.insert(0, head)
        return fmt_from_list_of_list(pl, sep=sep)
    else:
        return template('plan', title='All Plans', year=datetime.now().year)


@route('/ajax/plan')
def ajax_plan_list():
    if not request.is_ajax:
        return 'ajax api'
    if request.query.get('mac'):
        mac_filter = [i.strip() for i in request.query.get('mac').split(',')]
    else:
        mac_filter = None
    plan_list = plan.list(mac_filter)
    return dict(plan_list=[
        dict(
            rack=p.client.rack,
            location=p.client.location,
            mac=p.client.mac,
            progress_text=p.progress.text,
            progress_value=p.progress.value,
            finished=p.finished) for p in plan_list
    ])


@route('/plan/create')
@view('plan_create')
def plan_create():
    """Renders the plan create page."""
    return dict(
        title='Create PXE Plan for client', year=datetime.now().year, app=app)


@route('/plan/create', method='POST')
def plan_create_post():
    """Process the plan create post request."""
    rack = request.forms.get('rack')
    location = request.forms.get('location')
    mac = request.forms.get('mac')
    pxe_list = request.forms.get('pxe_list')
    if ((not mac) or mac == '' or
        (not re_mac.match(mac))) or ((not pxe_list) or pxe_list.strip() == ''):
        app.flash('MAC and Plan can\'t be empty')
        redirect('/plan/create')
    pxe_list = [
        i.split(',')[:2] for i in pxe_list.split('\n')
        if i.strip() != '' and ',' in i
    ]
    plan.create(rack=rack, location=location, mac=mac, pxe_list=pxe_list)
    redirect('/plan/view/%s' % mac)


@route('/plan/view/<mac>')
@view('plan_view')
def plan_view(mac):
    """Renders the plan view page."""
    check_mac_exists(mac)
    return dict(title='View Plan', year=datetime.now().year, mac=mac)


@route('/ajax/plan/view/<mac>')
def ajax_plan_view(mac):
    if not request.is_ajax:
        return 'ajax api'
    check_mac_exists(mac)
    p = plan.read(mac)
    plan_content = ['%s,%s' % (i.pxe, i.cycle) for i in p.pxe_list]
    plan_content = '\n'.join(plan_content)
    cfg_content = plan.pxe_cfg.get_pxe(mac)
    cfg_content = cfg_content if cfg_content else ''
    pxe_list = [
        dict(
            pxe=i.pxe,
            cycle=i.cycle,
            count=i.count,
            order=i.order,
            log=[
                dict(count=j.count, time=j.time.strftime('%Y/%m/%d %H:%M:%S'))
                for j in i.log
            ]) for i in p.pxe_list
    ]
    p2 = dict(
        mac=mac,
        client=dict(rack=p.client.rack, location=p.client.location),
        finished=p.finished,
        progress=dict(text=p.progress.text, value=p.progress.value),
        plan_content=plan_content,
        cfg_content=cfg_content,
        pxe_list=pxe_list)
    return dict(plan=json.dumps(p2))


@route('/plan/edit/<mac>')
@view('plan_edit')
def plan_edit(mac):
    """Renders the plan edit page."""
    check_mac_exists(mac)
    p = plan.read(mac)
    c = p.client
    content = ['%s,%s' % (i.pxe, i.cycle) for i in p.pxe_list]
    content = '\n'.join(content)
    return dict(
        title='Edit Plan',
        year=datetime.now().year,
        rack=c.rack,
        location=c.location,
        mac=c.mac,
        plan=plan.read(mac),
        content=content,
        app=app)


@route('/plan/edit', method='POST')
def plan_edit_post():
    """Process the plan edit post."""
    rack = request.forms.get('rack')
    location = request.forms.get('location')
    mac = request.forms.get('mac')
    pxe_list = request.forms.get('pxe_list')
    if (not mac) or mac == '' or (not re_mac.match(mac)):
        app.flash('MAC is empty or not valid')
        redirect('/plan')
    check_mac_exists(mac)
    if ((not pxe_list) or pxe_list == ''):
        app.flash('Plan can\'t be empty')
        redirect('/plan/edit/%s' % mac)
    pxe_list = [
        i.split(',')[:2] for i in pxe_list.split('\n')
        if i.strip() != '' and ',' in i
    ]
    # user may modify cfg file/cycle, so we just ignore all past pxe file but create a new one
    plan.create(
        rack=rack, location=location, mac=mac, pxe_list=pxe_list, time=None)
    redirect('/plan/view/%s' % mac)


@route('/ajax/plan/edit/<mac>')
def ajax_plan_edit(mac):
    if not request.is_ajax:
        return 'ajax api'
    check_mac_exists(mac)
    p = plan.read(mac)
    return dict(
        mac=p.client.mac,
        pxe_list=[
            dict(pxe=i.pxe, cycle=i.cycle, count=i.count) for i in p.pxe_list
        ])


@route('/plan/delete/<mac>')
@view('plan_delete')
def plan_delete(mac):
    """Renders the plan delete page."""
    check_mac_exists(mac)
    c = plan.read(mac).client
    return dict(
        title='Delete Plan',
        year=datetime.now().year,
        rack=c.rack,
        location=c.location,
        mac=c.mac)


@route('/plan/delete', method='POST')
def plan_delete_post():
    """Process the plan delete post."""
    mac = request.forms.get('mac')
    check_mac_exists(mac)
    plan.delete(mac)
    redirect('/plan')


@route('/plan/ack/<mac>')
def plan_ack(mac):
    """Process the plan ack."""
    check_mac_exists(mac)
    plan.ack(mac)
    redirect('/plan/view/%s' % mac)


@route('/plan/history/<mac>')
@view('plan_history')
def plan_history(mac):
    """Renders the plan history page."""
    return dict(
        title='Plan History',
        year=datetime.now().year,
        mac=mac,
        logs=plan.get_all_log(mac))


@route('/cfg')
@view('cfg')
def cfg_list():
    """Renders the cfg list page."""
    return dict(
        title='All cfgs',
        year=datetime.now().year,
        cfg_path=cfg.cfg_path(),
        pxe_path=cfg.pxe_path(),
        cfgs=plan.pxe_cfg.list_cfg())


@route('/ajax/cfg')
def ajax_cfg_list():
    if not request.is_ajax:
        return 'ajax api'
    return dict(cfgs=plan.pxe_cfg.list_cfg())


@route('/cfg/create')
@view('cfg_create')
def cfg_create():
    """Renders the cfg create page."""
    return dict(title='Create PXE cfg file', year=datetime.now().year, app=app)


@route('/cfg/create', method='POST')
def cfg_create_post():
    """Process the cfg create post request."""
    path = request.forms.get('path')
    content = request.forms.get('content')
    if ((not path) or path == '' or (not content) or content == ''):
        app.flash('Path and Content can\'t be empty')
        redirect('/cfg/create')
    plan.pxe_cfg.new_cfg(path, content)
    redirect('/cfg/view/%s' % path)


@route('/cfg/view/<path:path>')
@view('cfg_view')
def cfg_view(path):
    """Renders the cfg view page."""
    content = plan.pxe_cfg.get_cfg(path)
    if not content:
        abort(404)
    return dict(
        title='View cfg', year=datetime.now().year, path=path, content=content)


@route('/cfg/edit/<path:path>')
@view('cfg_edit')
def cfg_edit(path):
    """Renders the cfg edit page."""
    content = plan.pxe_cfg.get_cfg(path)
    if not content:
        abort(404)
    return dict(
        title='Edit cfg',
        year=datetime.now().year,
        path=path,
        content=content,
        app=app)


@route('/cfg/edit', method='POST')
def cfg_edit_post():
    """Process the cfg edit post."""
    path = request.forms.get('path')
    content = request.forms.get('content')
    if (not path) or path == '':
        redirect('/cfg')
    if (not content) or content == '':
        app.flash('Content can\'t be empty')
        redirect('/cfg/edit/%s' % (path))
    plan.pxe_cfg.new_cfg(path, content)
    redirect('/cfg/view/%s' % path)


@route('/cfg/delete/<path:path>')
@view('cfg_delete')
def cfg_delete(path):
    """Renders the cfg delete page."""
    content = plan.pxe_cfg.get_cfg(path)
    if not content:
        abort(404)
    return dict(title='Delete cfg', year=datetime.now().year, path=path)


@route('/cfg/delete', method='POST')
def cfg_delete_post():
    """Process the cfg delete post."""
    path = request.forms.get('path')
    if (not path) or path == '':
        redirect('/cfg')
    content = plan.pxe_cfg.get_cfg(path)
    if not content:
        abort(404)
    # plan.pxe_cfg.delete_cfg()
    redirect('/cfg')


# TODO: batch: add/delete/edit in batches
@route('/batch')
@view('batch')
def batch():
    """Renders the batch page."""
    return dict(
        title='batch',
        year=datetime.now().year,
    )


@route('/batch/plan/create')
@view('batch_plan_create')
def batch():
    """Renders the batch plan create page."""
    return dict(title='batch create plan', year=datetime.now().year, app=app)


@route('/batch/plan/create', method='POST')
def batch_plan_create_post():
    """Process the batch plan create post request."""
    rack = request.forms.get('rack')
    node = request.forms.get('node')
    mac_list = request.forms.get('mac_list')
    pxe_list = request.forms.get('pxe_list')
    if (not mac_list) or mac_list == '' or (
            not pxe_list) or pxe_list.strip() == '':
        app.flash('MAC and Plan can\'t be empty')
        redirect('/batch/plan/create')

    if node not in ['1', '2', '3']:
        node = '1'
    step = int(node)
    pxe_list = [
        i.split(',')[:2] for i in pxe_list.split('\n')
        if i.strip() != '' and ',' in i
    ]
    index = 0
    n_list = ['A', 'B', 'C'][:step]
    plan_created = []
    for mac in [i.strip() for i in mac_list.split('\n')]:
        n = n_list[index % step]
        if not re_mac.match(mac):
            continue
        if mac == '00:00:00:00:00:00':
            # dummy mac to indicate there is no node in this location
            index += 1
            continue
        location = '%02d%s' % (index / step + 1, n)
        plan.create(rack=rack, location=location, mac=mac, pxe_list=pxe_list)
        plan_created.append(location)
        index += 1

    app.flash('%d plan created/updated: %s' % (len(plan_created),
                                               ', '.join(plan_created)))
    redirect('/batch/plan/create')


@route('/log/download')
def log_download():
    """Renders the log download page."""
    file_path = plan.zip_logs()
    return static_file(file_path, root=cfg.log_path, download=file_path)


@route('/log/summary')
@view('log_summary')
def log_summay():
    """Renders the log download page."""
    return dict(
        title='log summary',
        year=datetime.now().year,
    )


@route('/ajax/log/summary')
@route('/ajax/log/summary/<mac>')
def ajax_log_summary(mac=None):
    if not request.is_ajax:
        return 'ajax api'

    plan_list = plan.list()
    if not mac:
        return dict(mac_list=json.dumps([p.client.mac for p in plan_list]))
    p = plan.read(mac)
    if not p:
        return dict(plan=json.dumps(None))

    def log_with_dt(log):
        last_ts = None
        new_log = []
        for i in log:
            if not last_ts:
                last_ts = i.time
            new_log.append(
                dict(
                    count=i.count,
                    dt=int((i.time - last_ts).total_seconds()),
                    time=i.time.strftime('%Y/%m/%d %H:%M:%S')))
            last_ts = i.time
        return new_log

    pxe_list = [
        dict(
            pxe=i.pxe,
            cycle=i.cycle,
            count=i.count,
            order=i.order,
            log=log_with_dt(i.log)) for i in p.pxe_list
    ]
    p2 = dict(
        mac=mac,
        client=dict(rack=p.client.rack, location=p.client.location),
        finished=p.finished,
        progress=dict(text=p.progress.text, value=p.progress.value),
        pxe_list=pxe_list)
    return dict(plan=json.dumps(p2))


@route('/draft')
@view('draft')
def draft():
    return dict(title='draft', year=datetime.now().year)


#
# helpers
#


def check_mac_exists(mac):
    if not plan.read(mac):
        abort(404)


def fmt_from_list_of_list(list_of_list, fmt='csv', **options):
    if fmt == 'csv':
        opt = {'sep': ','}
        opt.update(options)
        return '\n'.join(
            [opt['sep'].join([str(j) for j in i]) for i in list_of_list])
    else:
        return str(list_of_list)